package controller

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"server/models"
	"server/openapi"
	"server/utils"
	"strconv"
	"text/template"

	"github.com/gin-gonic/gin"
	"github.com/go-resty/resty/v2"
	"github.com/goinggo/mapstructure"
)

func DuAuthenticationPage(c *gin.Context) {
	response := models.Response{}
	var duPan = &models.DuPan{
		Host:   utils.Application.Dupan.Host,
		Path:   "/authorize",
		AppId:  utils.Application.Dupan.AppId,
		AppKey: utils.Application.Dupan.AppKey,
	}
	res, _, err := duPan.GetAuthQrcode()
	if err != nil {
		response.Code = ERR
		response.Msg = "failed"
		response.Data = err
	} else {
		response.Code = SUCC
		response.Msg = "success"
		response.Data = res
	}

	tmpl, err := template.New("index").Parse(res.String())
	if err != nil {
		c.String(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	// 创建一个字节数组，用于存储模板执行后的HTML内容
	var resultHTMLBytes []byte
	// 使用Execute方法将模板执行，并将结果写入resultHTMLBytes
	err = tmpl.Execute(c.Writer, nil)
	if err != nil {
		c.String(http.StatusInternalServerError, "Internal Server Error")
		return
	}
	// c.JSON(http.StatusOK, response)
	// 将结果HTML内容写入响应
	c.Data(http.StatusOK, "text/html; charset=utf-8", resultHTMLBytes)
}

func DuAuthenticationToken(c *gin.Context) {
	response := models.Response{}
	var duPan = &models.DuPan{
		Host:      utils.Application.Dupan.Host,
		Path:      "/token",
		AppId:     utils.Application.Dupan.AppId,
		AppKey:    utils.Application.Dupan.AppKey,
		SecretKey: utils.Application.Dupan.Secretkey,
	}
	res, _, err := duPan.GetAuthToken()
	if err != nil {
		response.Code = ERR
		response.Msg = "failed"
		response.Data = err
	} else {
		response.Code = SUCC
		response.Msg = "success"
		response.Data = res.String()
	}
	c.JSON(http.StatusOK, response)
}

func DuGetUserInfo(c *gin.Context) {
	response := models.Response{}
	api_client, accessToken := getApiClientFunc()
	_, r, err := api_client.UserinfoApi.Xpannasuinfo(context.Background()).AccessToken(accessToken).Execute()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error when calling `UserinfoApi.Xpannasuinfo``: %v\n", err)
		fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
	}
	bodyBytes, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", r)
	}
	// bodyString := string(bodyBytes)
	var m = make(map[string]interface{})
	err = json.Unmarshal(bodyBytes, &m)
	if err != nil {
		response.Code = ERR
		response.Msg = "failed"
		response.Data = err
	} else {
		response.Code = SUCC
		response.Msg = "success"
		response.Data = m
	}

	c.JSON(http.StatusOK, response)
}

func DuGetDocList(c *gin.Context) {
	response := &models.Response{}
	api_client, accessToken := getApiClientFunc()
	_, r, err := api_client.FileinfoApi.Xpanfiledoclist(context.Background()).AccessToken(accessToken).Execute()
	formatResponse(response, err, r)
	c.JSON(http.StatusOK, response)
}

func DuGetFileListNormal(c *gin.Context) {
	// dir
	response := &models.Response{}
	api_client, accessToken := getApiClientFunc()
	web := "1" // string |  (optional)
	dir := "/"
	if c.Query("dir") != "" {
		dir = c.Query("dir")
	}
	limit := int32(5) // int32 |  (optional)
	_, r, err := api_client.FileinfoApi.Xpanfilelist(context.Background()).AccessToken(accessToken).Dir(dir).Web(web).Limit(limit).Execute()
	formatResponse(response, err, r)

	doPanFile := models.DoPanFile{}

	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           &doPanFile,
		TagName:          "json",
	}

	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		panic(err)
	}

	if err := decoder.Decode(response.Data); err != nil {
		panic(err)
	}

	var file models.DoPanFileList
	for _, fileItem := range doPanFile.List {
		if fileItem.ServerFilename == utils.Application.Alipan.PasswordFileName {
			file = fileItem
		}
	}

	password := c.Query("password")

	if file.ServerFilename != "" && password == "" {
		response.Msg = "需要密码"
		response.Data = ""
		response.Code = 405
		c.JSON(http.StatusOK, response)
		return
	}

	if password != "" {
		fileResponse := &models.Response{}
		// 获取文件内容详情
		fileId := strconv.FormatInt(file.FsID, 10)
		thumb := "1"                // string |  (optional)
		extra := "1"                // string |  (optional)
		fsids := "[" + fileId + "]" // string
		dlink := "1"                // string |  (optional)
		_, r, err = api_client.MultimediafileApi.Xpanmultimediafilemetas(context.Background()).AccessToken(accessToken).Thumb(thumb).Extra(extra).Fsids(fsids).Dlink(dlink).Execute()
		formatResponse(fileResponse, err, r)
		// 通过dlink获取文本内容
		fileLink := fileResponse.Data.(map[string]interface{})["list"].([]interface{})[0].(map[string]interface{})["dlink"].(string) + "&access_token=" + accessToken
		resty := resty.New()
		fileTextRes, err := resty.R().Get(fileLink)
		if err != nil {
			panic(err)
		}
		fmt.Println(fileTextRes.String())
		// 获取

		if fileTextRes.String() != password {
			response.Msg = "密码错误"
			response.Data = ""
			response.Code = 405
			c.JSON(http.StatusOK, response)
			return
		}
	}

	c.JSON(http.StatusOK, response)
}

func DuGetFileDetail(c *gin.Context) {
	response := &models.Response{}
	fileId := c.Query("fileId")
	thumb := "1"                // string |  (optional)
	extra := "1"                // string |  (optional)
	fsids := "[" + fileId + "]" // string
	dlink := "1"                // string |  (optional)
	api_client, accessToken := getApiClientFunc()
	_, r, err := api_client.MultimediafileApi.Xpanmultimediafilemetas(context.Background()).AccessToken(accessToken).Thumb(thumb).Extra(extra).Fsids(fsids).Dlink(dlink).Execute()
	formatResponse(response, err, r)

	c.JSON(http.StatusOK, response)

}

// 获取文件播放链接
func GetPlayUrlByDlink(c *gin.Context) {
	// response := &models.Response{}
	dlink := c.Query("dlink")
	_, accessToken := getApiClientFunc()
	uri := dlink + "&access_token=" + accessToken
	resty := resty.New()
	res, err := resty.R().
		SetHeader("User-Agent", "pan.baidu.com").
		// SetHeader("Range", "bytes=0-0,-1").
		Get(uri)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}
	c.Header("Content-Type", "application/octet-stream")
	// 设置文件名
	c.Header("Content-Disposition", "attachment; filename=file.bin")
	c.Data(http.StatusOK, "application/octet-stream", res.Body())
}

func GetFileStreaming(c *gin.Context) {
	response := &models.Response{}
	duPan := models.DuPan{
		Host: "https://pan.baidu.com",
		Path: "/rest/2.0/xpan/file?method=streaming",
	}
	path := c.Query("path")
	adToken := c.Query("adToken")
	res, err := duPan.GetFileStreaming(path, adToken)

	if err != nil {
		response.Code = ERR
		response.Msg = "获取视频流失败"
	} else {
		response.Code = SUCC
		response.Msg = "success"
		var m = make(map[string]interface{})
		err := json.Unmarshal([]byte(res.String()), &m)
		if err != nil {
			fmt.Println("json解析失败")
			// json解析失败就是m3u8文件了
			c.Header("Content-Type", "application/x-mpegURL")
			c.String(http.StatusOK, string(res.Body()))
			return
		}
		response.Data = m
	}

	c.JSON(http.StatusOK, response)
}

func DuGetFileListRecursion(c *gin.Context) {
	response := &models.Response{}
	api_client, accessToken := getApiClientFunc()
	path := "/"           // string
	web := "1"            // string |  (optional)
	start := int32(0)     // int32 |  (optional)
	limit := int32(5)     // int32 |  (optional)
	recursion := int32(1) // int32 | (optional)
	_, r, err := api_client.MultimediafileApi.Xpanfilelistall(context.Background()).AccessToken(accessToken).Path(path).Web(web).Start(start).Limit(limit).Recursion(recursion).Execute()
	formatResponse(response, err, r)
	c.JSON(http.StatusOK, response)
}

func RefreshToken(c *gin.Context) {
	response := &models.Response{}
	response.Data = CommonRefresh()
	c.JSON(http.StatusOK, response)
}

func CommonRefresh() map[string]interface{} {
	api_client, _ := getApiClientFunc()
	workDir, _ := os.Getwd()
	refreshTokenValue, err := readTokenFromFile(workDir + "/DuPanRefresh")
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}

	var duPan = &models.DuPan{
		Host:      utils.Application.Dupan.Host,
		Path:      "/token",
		AppId:     utils.Application.Dupan.AppId,
		AppKey:    utils.Application.Dupan.AppKey,
		SecretKey: utils.Application.Dupan.Secretkey,
	}

	_, r, err := api_client.AuthApi.OauthTokenRefreshToken(context.Background()).RefreshToken(refreshTokenValue).ClientId(duPan.AppKey).ClientSecret(duPan.SecretKey).Execute()
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}
	bodyBytes, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}
	var jsonMap map[string]interface{}
	if err := json.Unmarshal(bodyBytes, &jsonMap); err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}
	// 7200s 两个小时后刷新token
	if jsonMap["access_token"] != "" {
		// 从文件中读取token
		workDir, _ := os.Getwd()
		writeTokenToFile(jsonMap["access_token"].(string), workDir+"/DuPanConfig")
	}
	if jsonMap["refresh_token"] != "" {
		// 从文件中读取token
		workDir, _ := os.Getwd()
		writeTokenToFile(jsonMap["refresh_token"].(string), workDir+"/DuPanRefresh")
	}
	return jsonMap
}

func formatResponse(response *models.Response, err error, r *http.Response) {
	if err != nil {
		response.Code = ERR
		response.Msg = err.Error()
	}
	bodyBytes, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", r)
	}
	var m = make(map[string]interface{})
	err = json.Unmarshal(bodyBytes, &m)
	if err != nil {
		response.Code = ERR
		response.Msg = "failed"
		response.Data = err
	} else {
		response.Code = SUCC
		response.Msg = "success"
		response.Data = m
	}
}

func getApiClientFunc() (*openapi.APIClient, string) {
	workDir, _ := os.Getwd()
	refreshTokenValue, err := readTokenFromFile(workDir + "/DuPanConfig")
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}
	accessToken := refreshTokenValue // string
	configuration := openapi.NewConfiguration()
	api_client := openapi.NewAPIClient(configuration)
	return api_client, accessToken
}

// 读取token
func readTokenFromFile(filename string) (string, error) {
	tokenBytes, err := os.ReadFile(filename)
	if err != nil {
		return "", err
	}
	token := string(tokenBytes)
	return token, nil
}

// 存储token

// 将token写入文件
func writeTokenToFile(token, filename string) error {
	// 打开文件
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("failed to create file: %v", err)
	}
	defer file.Close()
	// 写入token
	_, err = file.WriteString(token)
	if err != nil {
		return fmt.Errorf("failed to write to file: %v", err)
	}
	return nil
}
